

          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================


*-------------目录----------------
* 8.1 简介
* 8.2 暂元进阶功能
*   8.2.1 自动分解暂元的内容
*   8.2.2 暂元的文字处理和读讯功能
* 8.3 输入项
*   8.3.1 简单输入项  args 命令
*   8.3.2 复杂输入项  syntax 命令
*       变量名称
*       矩阵和单值
*       表达式
*       条件语句的设定
*       选项的设定
*       样本适用范围的设定
* 8.4 输出项
*   8.4.1 简介
*   8.4.2 r-class 程序
*   8.4.3 e-class  程序
* 8.5 可分组执行的程序
* 8.6 可重新显示结果的程序
* 8.7 子程序
* 8.8 程序的调试
* 8.9 帮助文件的编写
*   8.9.1 简介 
*   8.9.2 Stata 中的 smcl 语句
*   8.9.3 具有可链接目录的帮助文件
*   8.9.4 具有可运行实例的帮助文件
*--------------------------------


* 在学习本讲内容前，请先学习A5_Prog(Stata程序初步)一讲中的内容
help program

cd D:\stata15\ado\personal\Net_Course\B8_Prog2
adopath + "D:\stata15\ado\personal\Net_Course\B8_Prog2"


*==================
*  一个简单的实例
*==================

* 目的：计算给定变量的取值范围
  sysuse auto, clear
  sum price 
  local range = r(max) - r(min)
  dis "range = " `range'
  
* 改进 summarize 命令，使之可以呈现变量的取值范围
  
  doedit mysum0.ado
  
  sysuse auto, clear
  mysum0 price mpg weight length
//   ! taskkill /F /IM WINWORD.EXE /T
//   logout, save(Myfile) word replace: mysum0 price mpg weight length

        
* 小结：一个完整的程序应当包含如下几个部分
*    (1) 输入项，如变量名称、条件语句、样本范围、选项等
*         如：regress price weight length if foreign==1
*             sum price weight length in 1/50, detail
*    (2) 程序主体：计算、产生新变量、估计等
*    (3) 输出项
*    (4) 存储返回值 
*         如：return list, ereturn list
*    (5) 列示结果
*    (6) 有些时候，我们还需要编写程序的说明文件，如 help regress
*        对应文件为 regress.hlp   


*==================
*  暂元的进阶功能
*==================

*-- 自动分解暂元的内容

 *-- gettoken 命令：逐一提取单词
 
   * 提取 a 中的第一个单词至 y 中
   local a "price weight length"
   gettoken y: a
   dis "`y'"
   
   * 将 a 中的第一个单词放在 y 中，剩下的放在 x 中
   local a "price weight length"
   gettoken y x: a
   dis "`y'"
   dis "`x'"
   
   * 将 a 中的第一个单词移出至 y 中
   local a "price weight length"
   gettoken y a: a
   dis "`y'"
   dis "`a'" 
   
   
 *-- tokenize 命令：切割单词
   
   * 默认分割点为：空格                  
   local a "price weight length"
   tokenize "`a'" //该命令本质上是对字符串进行处理的。
   dis "`1'"
   dis "`2'"
   dis "`3'"
   
   * 指定分割点
   local b "price=3*weight/length"
   tokenize "`b'", parse("=*/")
   dis "`1'"
   dis "`2'"
   dis "`3'"  
   dis "`4'" 
   dis "`5'"
   dis "`6'"
   dis "`7'"
   * 练习：将选项修改为 parse("=*/")，结果有何差异？



*-- 暂元的文字处理和读讯功能  help extended_fcn

  *-- 暂元文字的处理
  
    *-- 文字结构：
      local m "I love Stata"
      local a: length local m  /*字母的个数*/
      local b: list sizeof m   /*单词的个数*/
      local c: word count `m'  /*单词的个数*/
      local d: word 3 of `m'   /*m 中的第三个单词*/
      local e: list posof "love" in m
      dis `a'
      dis `b'
      dis `c'
      dis "`d'"
      dis `e'
  
    *-- 文字替代：
      local m "I love Stata"
      local a: subinstr local m "love" "really love"
      local b: subinstr local m "a" "A"
      local c: subinstr local m "a" "A", all
      dis "`a'"
      dis "`b'"
      dis "`c'" 
      
    *-- 分隔字符清理：
      local m  " I   love     Stata " 
      local a: list retokenize m
      dis "`m'"
      dis "`a'"
      
    *-- 集合运算：
      local m "I love Stata!" 
      local n "She love stata too!"
      local a: list m | n //list只对单词进行操作。
      local b: list m & n
      local c: list m - n
      dis "`a'"
      dis "`b'"
      dis "`c'"
      
    *-- 文字比较：
      local m "I love Stata" 
      local n "Stata I love"
      local q "She said: I love Stata"
      local a: list m == n   /*单词和排序均相同*/
      local b: list m === n  /*单词相同*/
      local c: list m in q   /*m是否包含于q？*/
      dis `a'
      dis `b'
      dis `c'      
    
    *-- display 命令呈现的结果：
      display %6.4f ln(10)  // f: fixed
      local a: display %6.4f ln(10)
      dis `a'
    
    
  *-- 暂元的信息读取功能     
    
    *-- 变量信息：
    sysuse auto, clear
    describe
    local a: type   price
    local b: format price
    local c: variable label rep78   /*变量rep78的标签*/
    local d: value label foreign    /*foreign变量的数字-文字对应表*/
    local e: data label             /*样本标签*/
    local f: sortedby               /*样本排序变量*/
      dis "`a'"
      dis "`b'"
      dis "`c'"
      dis "`d'"
      dis "`e'"
      dis "`f'"  
      
    *-- 矩阵信息：
     mkmat price rep78 turn foreign in 1/10, mat(B)
     mat list B
     local a: rownames B
     local b: colnames B
      dis "`a'"
      dis "`b'"
    
    *-- 内存中的对象：
    scalar m = ln(100)
     local a: all globals
     local b: all scalars
     local c: all matrices
       dis "`a'"
       dis "`b'"
       dis "`c'"
    
    *-- 程序的返回值：
    sum price wei rep78 foreign
    return list
     local a1: r(scalars)
     local b1: r(macros)
     local c1: r(matrices)
     local d1: r(functions) 
       dis "`a1'"
       dis "`b1'"
       dis "`c1'"
       dis "`d1'"        
    reg price wei rep78 foreign
    ereturn list
     local a2: e(scalars)
     local b2: e(macros)
     local c2: e(matrices)
     local d2: e(functions)
       dis "`a2'"
       dis "`b2'"
       dis "`c2'"
       dis "`d2'"
    //如果用到了某个样本，则相应的 e(sample) 为 1       
    *-- 尚未使用的变量名称、暂时变量、或暂时性文件的名称：
    sysuse auto, clear
     local a: permname price   /*a中存储一个与price最为接近的变量名称*/
     local b: permname price, length(8) /*新变量名称最多可包含8个字母*/
     * 说明：length() 只能填写 8-32 之间的数值
       dis "`a'"
       dis "`b'"
           
    *-- 子目录信息：
     local a: dir . files "*"
     local b: dir "E:\stata系列\连玉君系列视频\高级版\" files "xt*"
     local c: dir . dirs "*"
       dis `"`a'"'
       dis `"`b'"'
       dis `"`c'"'     
     * 注意： `" "' 的使用方法 
	 //如果字符串里面有双引号，用绝对引用的方法也能让其很好的显示。










          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================
          *              8.3  输入项  
          
cd D:\stata15\ado\personal\Net_Course\B8_Prog2

*==================
*     输入项
*==================
* 8.3 输入项
*   8.3.1 简单输入项  args 命令
*   8.3.2 复杂输入项  syntax 命令
*       变量名称
*       矩阵和单值
*       表达式
*       条件语句的设定
*       选项的设定
*       样本适用范围的设定


*-- 简介

   sysuse auto,clear
   reg price wei len 
   reg price wei len if foreign==1
   reg price wei len in 1/50
   sum price wei len, detail 
   bysort foreign: sum price wei len


*-- 简单输入项  args 命令
  *--------------------------------------------------------
  cap program drop mysin
  program define mysin
  version 9.2
    args v1 v2
    local y = sin(`v1')^2 + cos(`v2')^2
    dis in g "sin(`v1')^2+cos(`v2')^2= " in yellow `y' 
  end
  *--------------------------------------------------------

  mysin 30 60
  mysin 45 45
  

*-- 复杂输入项  syntax 命令

  * 基本结构： syntax varlist  [if] [in] [,opt1() opt2()]
  *            syntax namelist [if] [in] [,opt1() opt2()]
  *            syntax anything [if] [in] [,opt1() opt2()]
  // 裸露中的逗号只能有一个逗号。
  *-- 变量名称
  
  * syntax varlist     输入暂元 varlist 中存储一个或多个变量名称
  * syntax varname     输入暂元 varlist 中仅存储一个变量名称
  * syntax newvarlist  输入暂元 varlist 中存储一个或多个新变量名称
  * syntax newvarname  输入暂元 varlist 中存储一个新变量名称
  
  *！ 注意：无论采用哪种定义方式，在程序中引用的输入暂元均为 varlist
  
  *=例1：针对每个变量进行回归
  *--------------------------------------------------------
  cap program drop mysepreg
  program define mysepreg
  version 9.2
    syntax varlist
    gettoken y x: varlist  /*分离出被解释变量和解释变量*/
    tokenize "`x'"         /*分隔解释变量为单个变量*/
    local i = 1
    while "``i''" != ""{   /*注意 ``i'' 的引用方式*/
      dis _n _n in y "Regression for " in w "``i''"
      reg `y' ``i++''
    }
  end
  *--------------------------------------------------------
  sysuse auto, clear
  mysepreg price wei len foreign
  mysepreg price mpg-turn
  
  
  * 改进该程序
  *--------------------------------------------------------
  cap program drop mysepreg2
  program define mysepreg2
  version 9.2
    syntax varlist
    gettoken y x: varlist  
    tokenize "`x'"         
    local i = 1
    while "``i''" != ""{   
      dis _n _n in y "Regression for " in w "``i''"
      reg `y' ``i''
      est store r_``i++''  /*存储各个回归结果*/
    }
    dis _n _n in w _dup(75)"="
    esttab r_*, b(%20.3f) se(%7.2f) star(* 0.10 ** 0.05 *** 0.01) ///
		r2(%9.3f) ar2(%9.3f) aic(%11.2f) bic(%11.2f) obslast scalars(F ll) ///
		sfmt(%7.2f %9.2f) nogaps compress title(esttab_Table: regression result)
  end
  *--------------------------------------------------------
  sysuse auto, clear
  mysepreg2 price wei len turn foreign
  


  
  *=例2：逐步回归法
 *--------------------------------------------------------
  cap program drop myrollreg
  program define myrollreg
  version 9.2
    syntax varlist
    gettoken y x: varlist
    local j = 1
    while "`x'" != "" {
      reg `y' `x'
      est store s_`j++'
      gettoken z x: x   /*每次剔除x中的一个变量*/
    }
	dis _n _n in w _dup(75)"="
    esttab s_*, b(%20.3f) se(%7.2f) star(* 0.10 ** 0.05 *** 0.01) ///
		r2(%9.3f) ar2(%9.3f) aic(%11.2f) bic(%11.2f) obslast scalars(F ll) ///
		sfmt(%7.2f %9.2f) nogaps compress title(esttab_Table: regression result)
  end    
 *--------------------------------------------------------
  
  sysuse auto, clear
  myrollreg price wei len turn rep78 foreign
  

  * 一些相关的设定：
  * syntax [varlist]       /*变量名称可选，如sum命令*/
  * syntax varlist (min=2) /*至少要输入量两个变量，如corr y x*/       
  * syntax varlist (min=2 max=5 string) /*变量个数必须介于2和5之间，且必须为字符型*/
  * syntax varlist [varlist(default=none)] /*默认情况下，不需输入变量名称*/
  * syntax newvarlist  /*必须为一组新变量的名称*/
  * syntax newvarname  /*必须为一个新变量名称,如 gen xx = ...*/
  *
  * 实例：
  *--------------------------------------------------------
  cap program drop mylog
  program define mylog
  version 9.2
    syntax varlist (min=2 max=4 numeric) 
    tokenize "`varlist'"
    local i = 1
    while "``i''" != ""{
      gen ln_``i'' = ln(``i++'')
    }
  end
  *--------------------------------------------------------
  sysuse auto, clear
  mylog  price                   /*变量个数太少*/
  mylog  price wei
  mylog  price wei len turn mpg  /*变量个数太多*/
  mylog  price make   /*make是文字型变量，不允许*/
  des
 
  
  *-- 矩阵及单值名称
  
  * syntax namelist 
  * syntax [namelist]
  * syntax namelist (max=2)  /*至多包含两个矩阵或单值*/
  * syntax name              /*只包含一个矩阵或单值*/
  
  *！ 注意：无论采用哪种定义方式，程序中调用的输入暂元均为 namelist
  
  *--------------------------------------------------------
  cap program drop myinvt
  program define myinvt
  version 9.2
    syntax namelist (max=3) 
    local j: word count `namelist'
    tokenize "`namelist'"
    forvalues i = 1(1)`j'{
      capture mat inv_``i'' = inv(``i'')
      if _rc{
        dis in error "Matrix ``i'' can not be inversed!"
      }
      else{
        mat list ``i''
        mat list inv_``i''
      }
    }   
  end
  *--------------------------------------------------------
  
  mat a = J(3,3,9)
  myinvt a
  matrix accum XX =  mpg wei length turn
  myinvt XX
  
  
  *-- 表达式
  
  * syntax anything
  * syntax anything (name=rr min=2)
  * syntax [anything (name==rr min=2 max=6)]
 *-------------------------------------------------------- 
  cap program drop mydis
  program define mydis
  version 9.2
    syntax anything
    local j : word count `anything'
    forvalues i = 1/`j'{
       dis _n in w "``i'' = "   in y `=``i''' _n  /*注意 `=`*'' 的使用方法*/
    }
  end
 *-------------------------------------------------------- 
  mydis 3+5/2  ln(3)*5^3
  mydis 3+5/2  ln(3)*5^3  sin(3)+cos(2) log(10)
  
  
  dis 3+5/2 _s(2) 5^3



          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================
          *              8.3  输入项    
          *                第二部分
          
  cd "D:\stata15\ado\personal\Net_course\B8_prog2\"
  *-- 条件语句的设定
  
  * syntax varlist if in
  * syntax varlist [if] [in]
  
  * 如：reg price wei len if foreign==1
  *     reg price wei len in 1/50
  
  * 基本思路：`if' 和 `in' 两个暂元中存储了相关的限制条件
 
 * 例1：if 和 in 暂元中包含的内容 
 *-------------------------------------- 
  cap program drop myif
  program define myif
  version 9.2
    syntax varlist [if] [in]
    dis "varlist 中包含： |`varlist'|"
    dis "     if 中包含:  |`if'|" 
    dis "     in 中包含:  |`in'|"
  end
 *-------------------------------------- 
  sysuse auto, clear
  myif price wei len 
  myif price wei len if foreign == 1
  myif price wei len in 1/10
  myif price wei len if foreign==0 in 1/50
  
  
 * 例2：mysepreg 命令的进一步改进
 *--------------------------------------------------------
  cap program drop mysepreg3
  program define mysepreg3
  version 9.2
    syntax varlist [if] [in]
    gettoken y x: varlist  
    tokenize "`x'"         
    local i = 1
    while "``i''" != ""{   
      dis _n _n in y "Regression for " in w "``i''"
      reg `y' ``i'' `if' `in'    /*！！注意此处的变化*/
      est store r_``i++'' 
    }
	dis _n _n in w _dup(75)"="
    esttab r_*, b(%20.3f) se(%7.2f) star(* 0.10 ** 0.05 *** 0.01) ///
		r2(%9.3f) ar2(%9.3f) aic(%11.2f) bic(%11.2f) obslast scalars(F ll) ///
		sfmt(%7.2f %9.2f) nogaps compress title(esttab_Table: regression result)
  end
 *--------------------------------------------------------
  
  sysuse auto, clear
  mysepreg3 price wei len turn foreign if price<10000 
  mysepreg3 price wei len in 20/60
  
  
  *-- 选项的设定
  * 基本结构： syntax varlist [, Opt1() Opt2() ....]
  * 例如： syntax ... , ... VALues(numlist max=10 sort) 
  *        syntax ... , ... Title(string) ...
  * 基本规则：选项标签的前几个大写字母表示该选项可被缩写的程度
  * help syntax
  *--------------------------------------------------------
  *！ 该程序是此前 mylog.ado 程序的扩展
  cap program drop mygen
  program define mygen
  version 9.2
    syntax varlist [, Math(string) ] 
    tokenize "`varlist'"
    local i = 1
    while "``i''" != ""{
      gen `math'_``i'' = `math'(``i++'')  /*注意选项 `math' 的引用，必须小写*/
    }
  end
  *--------------------------------------------------------
  help math functions
  sysuse auto, clear
  mygen price, math(sum)
  list price sum_price in 1/10
  mygen price wei len, math(ln)
  list price ln_price wei ln_wei len ln_len in 1/10
  
  
  * 多个选项的设定
  *--------------------------------------------------------
   *！此程序是此前 myinvt.ado 程序的扩展
   cap program drop myinvt2
   program define myinvt2
   version 9.2
    syntax namelist [,Title(string) DISplay ]  /*此处设定了两个选项*/
    if "`title'" != ""{
      dis _n in w "`title'"
    }
    local j: word count `namelist'
    tokenize "`namelist'"
    forvalues i = 1(1)`j'{
      cap mat inv_``i'' = inv(``i'')  /*检查矩阵是否可逆*/
      if _rc{                         /*若可逆，则_rc=0,否则_rc!=0*/
        dis in error "Matrix ``i'' can not be inversed!"
        exit  /*跳出程序*/
      }
      else {
        if "`display'" != ""{      /*此处，通过选项display来控制结果显示与否*/
          mat list ``i''
          mat list inv_``i''
        }
      }
    }   
   end
  *--------------------------------------------------------
  
  mat a = J(3,3,9)
  myinvt2 a
  sysuse auto, clear
  matrix accum XX =  mpg wei length turn
  myinvt2 XX  
  myinvt2 XX, t("Inverse a Matrix") di  /*错误！*/
  myinvt2 XX, t("Inverse a Matrix") dis   
  
  
  * 例：选取时间序列的一部分样本
   * 问题：按照开始时间和结束时间截取时间序列的一部分
     sysuse gdp.dta
     tsset year
     keep if  year>=1950 & year<=2004
  *--------------------------------------------------------
  cap program drop mycut
  program define mycut
  version 9.2
    syntax  [, range(numlist min=2 max=2 ascending) ]
    gettoken begin end: range   /*将 range 中的两个数字分贝存于暂元 begin 和 end 中*/
    capture tsset
    if _rc{
      dis as error "You must" in g " tsset " ///
          in red   "your data before using "  ///
          in g     "mycut"
      exit
    }
    else{
      local t  `r(timevar)'  /*将时间标识变量存储于暂元 t 中*/
    }
    keep if  `t'>=`begin' & `t'<=`end'
  end
  *--------------------------------------------------------
    use gdp.dta, clear
    tsset, clear
    mycut , range(1950 2004)  /*错误！*/
    tsset year 
    mycut , range(1950 2004)
    mycut , range(2006 1938)  /*选项中 ascending 的作用*/
  
  * 一些可能的改进途径：
    * 当用户输入的数值超出样本的始末时间时，提示错误信息
    * 对于 1990q1, 7jul1990 等格式的数据进行自动处理
    

*-- 样本适用范围的设定  help marksample ; help mark; help markout
  
  * 问题：多数stata命令可以自动忽略缺漏值，
  *       并针对用户设定的 [if] 或 [in] 条件来决定计算或估计中使用的样本
  * 这些功能主要通过 markt* 命令来实现
  * 基本思想：产生一个新变量(0/1)，由于记录符合条件的样本值
  *--------------------------------------------------------
  cap program drop myreg3
  program define myreg3
  version 9.2
    syntax varlist [if] [in] [, Title(string) Option(string)]
    marksample touse //符合 if 和 in，且没有缺漏值，touse 将被标记为 1，否则为 0。
    reg `varlist' if `touse'
  end
  *--------------------------------------------------------
  sysuse auto, clear
  myreg3 price wei len if rep78>2
   count if rep78>2
  myreg3 price wei len rep78 if rep78>2
   sum rep78
   count if  rep78>2 & rep78!=.
   // . > anything
  * 评论：相对于 reg `varlist' `if' `in'
  *       使用 marksample 命令更为简洁、安全。
  *       我们尚可使用如下程序获得相同的效果
  *       基本思路：
  *         先定义一个暂时性变量 touse，然后使用 mark 和 markout 命令标记之
  *--------------------------------------------------------
  cap program drop myreg4
  program define myreg3
  version 9.2
    syntax varlist [if] [in] [, Title(string) Option(string)]
    tempvar touse
    mark `touse' `if' `in'    /*标记 if 和 in 中的样本*/
    markout `touse' `varlist' /*标记 varlist 中包含缺漏值的样本*/ 
    reg `varlist' if `touse'
  end
  *--------------------------------------------------------  
  
  * 进一步的说明：
  * 1. marksample touse, novarlist /*不考虑varlist中的缺漏值*/
  * 2. marksample touse, strok     /*样本适用范围不受文字值的影响*/
  *    因此，我们可以在程序中设定多个样本标识，如
  *     syntax varlist [if] [in]
  *     marksample touse1
  *     marksample touse2, strok   
  * 3. 使用 markout 命令可以进一步将选项中的样本限制条件纳入分析,如：
  *--------------------------------------------------------
  cap program drop myreg5
  program define myreg5
  version 9.2
    syntax varlist [if] [in] [, option1(string) option2(string)]
    marksample touse
    markout `touse' `option1' `option2'  /*纳入option1和option2施加的约束*/
    reg `varlist' if `touse'
  end
  *--------------------------------------------------------     









          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================
          *              8.4  输出项  
          

*==================
*     输出项
*==================

* 8.4 输出项
*   8.4.1 简介
*   8.4.2 r-class 程序
*   8.4.3 e-class  程序


*-- 简介

  * Stata 程序分为四类: r-class、e-class、s-class 和 n-class
  *   前三种分别以 r(...)、e(...)、s(...)为名称存储结果；
  *   n-class 程序不会在内存中存储任何结果
  
  sysuse auto, clear
  sum price
  return list
  
  reg price wei len foreign
  ereturn list
  
  
*== r-class 程序 ==

  sysuse auto, clear
  sum price wei len foreign
  ret list
  
  *--------------------------------------------------------   
  *! 一个简单的 r-class 程序
  cap program drop mysum2
  program define mysum2, rclass
     version 9.2
     syntax varname     /*输入项*/
    * 存储单值
      qui sum `varlist' , detail
      local range = r(max)-r(min)
      local max_min = r(max) / r(min)
      return scalar range    = `range'
      return scalar max_min  = `max_min'
    * 存储暂元
      local a: variable label `varlist'
      local b: format `varlist'
      ret local vlabel  `a'
      ret local vformat `b'
    * 在屏幕上呈现输出结果
      dis in g "range = " in y %6.3f `range' 
     dis in g "max_min = " in y %6.3f `max_min'
  end   
  *-------------------------------------------------------- 
  
  sysuse nlsw88.dta, clear
  mysum2 wage
  ret list
  mysum2 occupation
  ret list
       
       
*== e-class 程序 ==
  
  sysuse nlsw88, clear
  reg wage age race married
  eret list

  *-------------------------------------------------------- 
  *! 一个简单的 e-class 程序(wmt 修正)
  cap program drop myreg6
  program define myreg6, eclass 
  version 9.2
  
    syntax varlist [if] [in]
    gettoken y x: varlist
    local df_m : word count `x'  /*模型的自由度*/
    marksample touse
    
    preserve
	qui keep if `touse'  /*注意该用法*/
      qui count if `touse'
      local N = r(N)       /*记录有效样本数*/
	  gen cons = 1
      tempname Y X b XX
      mkmat `y', mat(`Y')
      mkmat `x' cons, mat(`X')
	  //mat `XX' = `X''*`X' //也是符合临时变量的定义
      mat `b' = inv(`X''*`X')*`X''*`Y'  /*b=inv(X'X)X'y*/
    restore
    
    * 显示结果
    dis _n in g "OLS regress for " in y "`y'" in g " on " in y "`x':" 
    mat list `b',noheader
	//矩阵的行名和列名刚好是符合要求的，所以不用再进行设置
// 	mat list `Y'
// 	mat list `X'
// 	mat list `XX'
    
    * 存储返回值：函数
      eret post, esample(`touse')
    * 存储返回值：单值
      ereturn scalar N    = `N'
      eret    scalar df_m = `df_m'
    * 存储返回值：暂元
      eret local cmd "myreg6"
      eret local dep_var   `y'
      eret local indep_var `x'
    * 存储返回值：矩阵
      eret matrix beta = `b'
  end
  *--------------------------------------------------------           

  sysuse auto, clear
  ereturn clear  /*清除内存中已有的返回值*/  
  myreg6 price wei len foreign
  eret list
  dis e(cmd)
  mat list e(beta)

  
  
  
  * 几点说明：
  * (1) 若程序中调用了stata现有命令，则这些程序的返回值也可以使用；
  * (2) 良好习惯：所有待返回的值先存储于暂元中，然后在程序结束前一并存储
  * (3) 使用细节参见: help ereturn 
 
 
  * ereturn post 命令：借用stata的格式显示结果
    * 问题：上面的结果显示方式不够美观
    * 解决：可以借用stata的结果显示方式

  *-------------------------------------------------------- 
  *! 一个较为完整的 e-class 程序
  cap program drop myreg7
  program define myreg7, eclass 
  version 9.2
  
    syntax varlist [if] [in] [, level(real 95) eform(string)]
    gettoken y x: varlist
    local df : word count `x'  /*模型的自由度*/
	local dfa = `df' + 1
    marksample touse
      qui count if `touse'
      local nobs = r(N)      /*记录有效样本数*/
    preserve
      qui keep if `touse'    /*注意该用法*/
	  gen cons = 1
      tempname Y X b e t0 V 
      mkmat `y', mat(`Y')
      mkmat `x' cons, mat(`X')
      mat `b' = inv(`X''*`X')*`X''*`Y'  /*b=inv(X'X)X'y*/
      mat `e' = `Y' - `X'*`b'
      mat `t0' = `e''*`e'    /*残差平方和*/
      local sigma2 = `t0'[1,1]/(`nobs'-`dfa')  /* s2=e'e/(N-K-1) */
      mat `V' = `sigma2'*inv(`X''*`X')  /*beta的方差-协方差矩阵 V=s2*inv(X'*X)*/
    restore

    * 显示结果
      mat `b' = `b''
      dis _n in g "OLS regress for " in y "`y'" in g " on " in y "`x':" 
      eret post `b' `V', esample(`touse') obs(`nobs') dof(`df') depname(`y')
      eret display, level(`level') eform(`eform')
    * 存储更多的返回值：暂元
      eret local cmd "myreg6"
      eret local dep_var   `y'
      eret local indep_var `x'
  end
  *--------------------------------------------------------              
  
    sysuse auto, clear
    ereturn clear  /*清除内存中已有的返回值*/  
    myreg7 price wei len foreign
  * 改变置信区间
    myreg7 price wei len foreign, level(99)
  * 以指数形式呈现估计系数
    myreg7 price wei len, eform(ecof)
    
  * =小结：
  *  (1) 在多数情况下，只要我们能计算出 b 和 V 矩阵，
  *      便可采用 ereturn post 命令列示结果，stata会自动完成相关的计算。
  *  (2) 在执行完 ereturn post 后，才能进一步采用 eret local 存储其它返回值，
  *      这是因为 eret post 会清除此前已经存储的所有返回值；
  *  (3) 至于结果的表头信息，如R2,F值等信息，则需要自行计算并列表呈现。
  
  











          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================
          *         8.5 可分组执行的程序
          *         8.6 可重新显示结果的程序
          *         8.7 子程序
          *         8.8 程序的调试 
          
          

*==================
* 可分组执行的程序
*==================

* 例如：
  sysuse nlsw88, clear
  bysort race : sum wage
  bysort married: reg wage race age hours

*-- 方式1：在程序首行附加 byable(recall) 选项即可

* 条件: 
*   a. 程序中包含 marksample 命令 (或功能相似的 mark 命令)，
*      此时必然定义了一个样本范围的指针变量 touse
*   b. 程序执行完毕后不会产生任何新变量；

* 例：
  *-------------------------------------------------------- 
  *! 一个可分组执行的 e-class 程序
  cap program drop myreg8
  program define myreg8, eclass byable(recall) /*注意：此处增加了 byable() 选项*/ 
  version 9.2
  
    syntax varlist [if] [in] [, level(real 95) eform(string)]
    gettoken y x: varlist
    local df : word count x  /*模型的自由度*/
    marksample touse
      qui count if `touse'
      local nobs = r(N)      /*记录有效样本数*/
    preserve
      qui keep if `touse'    /*注意该用法*/
	  tempvar cons
	  gen `cons' = 1
      tempname Y X b e t0 V 
      mkmat `y', mat(`Y')
      mkmat `x' `cons', mat(`X')
      mat `b' = inv(`X''*`X')*`X''*`Y'  /*b=inv(X'X)X'y*/
      mat `e' = `Y' - `X'*`b'
      mat `t0' = `e''*`e'    /*残差平方和*/
      local sigma2 = `t0'[1,1]/(`nobs'-`df')  /* s2=e'e/(N-K) */
      mat `V' = `sigma2'*inv(`X''*`X')  /*beta的方差-协方差矩阵 V=s2*inv(X'*X)*/
    restore

    * 显示结果
      mat `b' = `b''
      dis _n in g "OLS regress for " in y "`y'" in g " on " in y "`x':" 
      eret post `b' `V', esample(`touse') obs(`nobs') dof(`df') depname(`y')
      eret display, level(`level') eform(`eform')
    * 存储更多的返回值：暂元
      eret local cmd "myreg6"
      eret local dep_var   `y'
      eret local indep_var `x'
  end
  *--------------------------------------------------------  
  
  sysuse nlsw88, clear
  bysort race : myreg8 wage hours ttl_exp 
  //有问题，除了系数之外其他结果不一致，可能是由于产生了新变量的原因
  bysort race : reg wage hours ttl_exp, r


*-- 方式2：若程序中未包含 marksample 指令，但不会产生新变量

* 只需在程序中
* (1) 使用 tempvar 命令产生一个暂时性变量 touse,
* (2) 继而增加如下指令，以定义一个“分组样本使用范围指针”
*-----------------------------------------------------------
*  if _by(){
*    quietly replace `touse'=0 if `_byindex' != _byindex()
*  }
*-----------------------------------------------------------
* 释义：
*  _by() 和 _byindex() 都是函变量，_byindex 是暂时性变量，
*  定义如下：
*               { 1， 若程序被要求分组执行；
*       _by() = { 
*               { 0， otherwise。 
*  
*               { k， 若程序被要求对第 k 分组执行；
*  _byindex() = {
*               { 0， 若程序未被要求可分组执行。
*
*               { 1,2,3,...  若程序被要求可分组执行； 
*    _byindex = { 
*               { 空         otherwise

* 例：针对每个变量分别进行回归(可分组执行)
  *--------------------------------------------------------
  cap program drop mysepreg2
  program define mysepreg2, byable(recall)
  version 9.2
    syntax varlist
    gettoken y x: varlist  /*分离出被解释变量和解释变量*/
    tokenize "`x'"         /*分隔解释变量为单个变量*/
    *---------  
     tempvar touse     /*新增语句*/
     gen `touse' = 0   /*新增语句*/
     if _by(){         /*新增语句*/
       quietly replace `touse'=0 if `_byindex' != _byindex()
     }
    *---------
    local i = 1
    while "``i''" != ""{   /*注意 ``i'' 的引用方式*/
      dis _n _n in y "Regression for " in w "``i''"
      reg `y' ``i++''
    }
  end
  *--------------------------------------------------------
  
  sysuse nlsw88, clear
  bysort married : mysepreg2 wage hours race
  //有问题，并不能真正的 bysort，依然按照总体进行回归。


*========================
*  可重新显示结果的程序
*========================  

* 多数stata程序都可以两种方式执行：
* 方式1：指定输入项；
* 方式2：不指定输入项，重新显示最近一次的结果。
* 如， 
  sysuse auto, clear
  reg price wei len 
  reg
    
* 处理方式：
*     在程序中附加 "if replay() ..." 函数的判断即可

* 例：
  *-------------------------------------------------------- 
  *! 一个可重新显示结果的程序
  cap program drop myreg9
  program define myreg9, eclass
  version 9.2
    if replay(){
       if "`e(cmd)'" != "myreg9"{  /*程序名称*/
             error 301
       }
	       `e(cmd)' `e(varlist)' `if' `in' 			
    }  
    else{  
       syntax varlist [if] [in] [, Title(string) Option(string)]
       marksample touse
       reg `varlist' if `touse'
       eret local varlist `varlist'
       eret local cmd "myreg9"     /*设定返回值：程序名称*/
    }
  end
  *--------------------------------------------------------

  sysuse auto, clear
  myreg9 price wei len mpg
  sum 
  d
  reg price wei
  myreg9 
  myreg11  /*错误！*/
  
  

*==================
*     子程序
*==================  
  cd "D:\stata15\ado\personal\Net_course\B8_prog2\"
* 问题：
*  (1) 复杂程序切割成多个模块，便于程序的编写和调试；
*  (2) 程序中的某些功能需要反复调用，这些功能应该制作成子程序；
*  (3) 子程序可内置于主程序中，亦可单独存放于相关目录下；

* 范例：
       viewsource xtreg.ado
         viewsource xtreg_fe.ado
         viewsource xtreg_re.ado
         viewsource xtreg_ml.ado 
 
* 例：不同风格的标题
  *--------------------------------------------------------            
   cap program drop mytitle
   program define mytitle
   version 9.2
      syntax , Title(string) [Scheme(string)]
      if "`scheme'" == ""{
         mydis0, ti("`title'")  /*默认风格*/
      }
      else if "`scheme'" == "m2"{
         mydis2, ti("`title'")  /*备选风格*/
      }
      else{
         dis in error "shceme() can only specifies as" in y " m2"
      }
   end
   
   * 第一个子程序
     cap program drop mydis0
     program define mydis0
     version 9.2
       syntax , ti(string)
       dis in g "--- " in y "`ti'" in g " ---"
     end  
     
   * 第二个子程序
     cap program drop mydis2
     program define mydis2
     version 9.2
       syntax , ti(string)
       dis _n in y "&&&&&&&& " in w "`ti'" in y " &&&&&&&&" _n
     end    
  *--------------------------------------------------------
  
  mytitle, title(I love stata!)            /*默认风格*/ 
  mytitle, title(I love stata!) scheme(m2) /*备选风格*/
  mytitle, ti(I love stata!) scheme(m3)    /*错误设定*/
  
  
  *== 练习：尝试编写一个程序，以便实现如下功能 
   tab_title, ti(I Love Stata!)
   tab_title, ti(I Love Stata!) tcolor(input) lcolor(error) 
   tab_title, ti(This is the firt program I wrote using Stata! I am very exciting now)  ///
              tcolor(result) lcolor(text)  
   tab_title, ti(I love stata!) tc(blue) /*错误信息*/
  *== 答案：
           viewsource tab_title.ado
      
      
      
*==================
*    程序的调试
*==================
   
  *== set trace on/off
   
  *--------------------------------------------------------
  cap program drop myinvt
  program define myinvt
  version 9.2
    syntax namelist (max=3) 
    local j: word count `namelist'
    tokenize "`namelist'"
    forvalues i = 1(1)`j'{
      cap mat inv_``i'' = inv(``i'')
      if _rc{
        dis in error "Matrix ``i'' can not be inversed!"
      }
      else{
        mat list ``i''
        mat list inv_``i''
      }
    }   
  end
  *--------------------------------------------------------
  
  set trace on 
  mat a = J(3,3,9)
  myinvt a
  
  set trace on
  matrix accum XX =  mpg wei length turn
  myinvt XX
   
  *== 一些经验：
  * (1) 检查暂元的引用是否正确？ 
  *     如，当使用 tokenize 命令时，需要采用 ``1'' 的方式加以引用；
  * (2) 是否存在拼写错误？
  * (3) {} 或 () 是否匹配? 尤其是 if，forvalues, while 等语句；
  * (4) 程序的架构和逻辑是否合理？
   
   
 
 
 


          ********* 计量分析与STATA应用 *********

          *        主讲人：连玉君 博士

          *        单  位：中山大学岭南学院金融系
          *        电  邮: arlionn@163.com
          *        主  页: http://blog.cnfol.com/arlion 
          
          *              ::高级部分::
          *          计量分析与Stata应用
          *       ==========================
          *          第八讲  STATA编程进阶   
          *       ==========================
          *           8.9 帮助文件的编写  
          
          
cd D:\stata15\ado\personal\Net_Course\B8_Prog2
adopath + "D:\stata15\ado\personal\Net_Course\B8_Prog2"   
      
*==================
*  帮助文件的编写
*==================
* 8.9 帮助文件的编写
*   8.9.1 简介 
*   8.9.2 Stata 中的 smcl 语句
*   8.9.3 具有可链接目录的帮助文件
*   8.9.4 具有可运行实例的帮助文件


*== 简介 ==

help regress
viewsource regress.sthlp

help gqhet
viewsource gqhet.hlp


*== 简单方法：复制已有帮助文件，修改之 ==

  * 假设我们要为 myreg6.ado 命令编写帮助文件：
    * 第一步：打开一份风格相似的帮助文件，如 regress.sthlp(D:\stata15\ado\base\r)，
              另存至 D:\stata15\ado\personal\Net_Course\B8_Prog2 文件夹中，
              将文件名修改为 myreg6.sthlp;
    * 第二步：编辑 myreg6.sthlp 文件，修改其内容；
    * 第三步：保存 myreg6.sthlp 文件；
    * 第四步：在命令窗口中输入 help myreg6 即可查看该帮助文件的内容。
  * 说明：在stata9以下的版本中，stata的帮助文件以 .hlp 为后缀，其它设定相似。
    
                

*== Stata 中的 smcl 语句 ==  help smcl
  
  * smcl (Stata Markup and Control Language) 
  * 用途：以特定的格式显示文字
  
  *-- 格 式 --
  * 基本设定： {格式代码: 文字}
  
    * 标题
      dis "{title: I Love Stata!}"  
    * 带边框的子标题
      dis "{dlgtab:Main}"  
      dis "{dlgtab:Example}" 
    * 下划线(underline--ul)
      dis "see {ul:[xt]xtreg} for more details"  
    * 斜体
      dis "I {it:Love} stata!"
    * 粗体
      dis "I {bf:love} stata!"
    * 高亮显示
      dis "{hi:I love} stata!"
      dis "{hilite}I love stata!"
    * 直线
      dis "{hline}"  
      dis "{hline 20}  {hline 40}"  //占 20 空格和占 40 的空格
      dis "{.-}"          
      dis "{hline} {hi: help myreg3} {right: Version 1.1} {hline}" 
    * 字体颜色
      dis "{input:She} {error:Love} {result:Stata} {text:very much!}"
    * 帮助文件中的注释语句
      dis "{* this text will be ignored}"
    * 命令的显示：加粗
      dis "you can use {cmd:xtreg} command"     
    * 命令的显示：缩写
      dis "you can use {cmdab:xt:reg} command" 
    * 选项：
      dis "{opt option(a)}"
      dis "{opt option(a|b)}"
      dis "{opt option1: option2}"  
      dis "{opt option1: option2(a,b)}"
    * 重复显示
      dis "{dup 5: Stata }"
              
  *-- 位 置 --
      
    * 居中
      dis "{center: I Love Stata!}"
    * 右对齐
      dis "{right: version 1.0}"   
    * 占#个空格左(右)对齐：相当于一个隐形的文本框
      dis "{lalign 12:mpg}{ralign 15: 21.897}"
      dis "|{lalign 12:mpg}|{ralign 15: 21.897}|"
    * 一定长度的空格(较少使用)
      dis "while {...} You can {...}{...} specify another variable"
    * 指定在第几列显示
      dis "{col 20}Mean"
	  dis _s(19) "Mean"
      dis "{hline 60}" _n "{col 3}Variable{col 18}Mean{col 30}Min{col 55}Max" _n "{hline 60}"
      dis "F{col 9}= 30.6"
      dis "p-value{col 9}= 0.000"
    * 指定空格数目
      dis "F = {space 10}12000"      
      dis "F =           12000"
    * -tab-键：相当于6个空格的间距
      dis "x1{tab}x2{tab}x3" 	
	  dis "X1{space 6}X2{space 6}X3"
  *-- 超链接 --
  
    * 简单帮助链接
      dis "see {help regress} for details"
    * 带有文字标签的帮助链接  
      dis "see help {help xtreg: Panel Data}"  
    * 对话方式的帮助链接
      dis "see help {help_d: help system}"
    * 自动搜索的帮助链接
      dis "{search panel data: click here} for the latest information for panel data"  
    * Stata手册链接
      dis "{manhelp summarize XT}"
    * 对话方式的搜索链接
      dis "for details, using the {search_d: search system}"
    * 网址或E-mail超链接: 链接文字
      dis `"欢迎提出建议，请登陆：{browse "http://blog.cnfol.com/arlion":Arlion 的博客}"'
      dis `"you can {browse "http://www.stata.com":visit the Stata web site}"'
    * 网址或E-mail超链接: 链接网址  
      dis `"see {browse "http://www.stata.com/man/readme.smcl"}"'
      dis `"请登陆：{browse "http://blog.cnfol.com/arlion"}"'
    * 外部 ado 命令超链接  
      dis "you can see the user-written package by {ado dir: clicking here}"
    * 对话方式外部 ado 命令查询  
      dis "you can search user-written ado files by {ado_d: click here}"
    * 返回前页  
      dis "{back: go back to the previous page}"
    * 可执行的stata命令链接(以 stata 开头即可)
      dis "Example: {stata sysuse auto, clear}"
      dis "         {stata summarize price}"
     
  *-- 段 落 --
  
    * 段落标识： {p}
     * 完整设定格式： {p # # #}
     * 第一个 # : 首行缩进量
     * 第二个 # : 第二行和后续各行的缩进量
     * 第三个 # : 所有行的右侧缩进量
     * 示例：
     *  {p}
     *  {p 4}
     *  {p 0 4}
     *  {p 8 8 8}
     
    * 一些固定的段落格式
    
    *          SMCL directive ...   is equivalent to typing ...
    *          --------------------------------------------------
    *          {pstd}               {p 4 4 2}
    *          {psee}               {p 4 13 2}
    *          {phang}              {p 4 8 2}
    *          {pmore}              {p 8 8 2}
    *          {pin}                {p 8 8 2}
    *          {phang2}             {p 8 12 2}
    *          {pmore2}             {p 12 12 2}
    *          {pin2}               {p 12 12 2}
    *          {phang3}             {p 12 16 2}
    *          {pmore3}             {p 16 16 2}
    *          {pin3}               {p 16 16 2}
 
    
    * {p_end} 段落结束标志
    
    * {bind:text} 强制性地将 text 文字显示在同一行 
      dis "Commonly, {ul:bind} is used {space 60}{bind:to keep words together} on a line"
      dis "Commonly, {ul:bind} is used {space 60}to keep words together on a line"
      
    * {break} 强制性地断行
      dis "{it:Example:}{break} Commonly, we can use ..."  
      
 
  *-- 特殊字符 --
  
    * 基本格式： {c #} 或 {c ...}
    
    * 一些常用的符号：
      * {c S|}  $ (美元符号)
      * {c 'g}  ` (左撇号)
      * {c -(}  { (左花括号)
      * {c )-}  } (右花括号)
        dis "I have 35$"       /*可正常显示*/
        dis "Shown in $US"     /*无法正常显示*/
        dis "Shown in {c S|}US"
        
        dis "list the following numbers: {1, 2, 4, 7}"
        dis "in the set {result}"  /*无法正常显示*/
        dis "in the set {c -(}result{c )-}" 
    
    * 特殊线型：主要用于制作表格
      * {c -}     a wide dash character  
        dis "  {c -}"
      * {c |}     a tall |
        dis "  {c |}"
      * {c +}     a wide dash on top of a tall |
        dis "  {c +}"
      * {c TT}    a top T 
        dis "  {c TT}"
      * {c BT}    a bottom T 
        dis "  {c BT}"
      * {c LT}    a left T 
        dis "  {c LT}"
      * {c RT}    a right T
        dis "  {c RT}"
      * {c TLC}   a top-left corner  
        dis "  {c TLC}"
      * {c TRC}   a top-right corner 
        dis "  {c TRC}"
      * {c BRC}   a bottom-right corner
        dis "  {c BRC}"
      * {c BLC}   a bottom-left corner
        dis "  {c BLC}"
  

      dis "{c -}"
      dis "{c -} {hline 20}"
      dis "{c |}"
      dis "{c +}"     
      dis "{hline 15}{c +}{hline 16}" 
      dis "{hline 30}" _n "{space 15}{c |}" _n "{hline 15}{c +}{hline 14}" 
      dis "{c TT}"
      dis "{c LT}"
      dis "{hline 30}{c RT}"

    *- 一个较为复杂的例子
      #delimit ;
        dis  "{c TLC}{hline 30}{c TRC}" _n  
             "{c |}  {space 28}{c |}"   _n 
             "{c BLC}{hline 30}{c BRC}" ;
      #delimit cr  
    *- 元素解析
      #delimit ;
        dis  "{c TLC} {hline 30} {c TRC}" _n  
             " {c |}  {space 28}{c |}"   _n 
             "{c BLC} {hline 30} {c BRC}" ;
      #delimit cr 
              
    * 特殊字符
      * set charset mac
      set charset latin1     
        dis "{c a'}"  
        dis "{c A'}"
        dis "{c a'g}"
        dis "{c A'g}"
        dis "{c a^}"
        dis "{c A^}"
        dis "{c a~}"
        dis "{c A~}"
        dis "{c a:}"
        dis "{c A:}"    
      
        dis "{c L-}"
        dis "{c ss}"
        dis "{c Y=}"
        dis "{c o/}"
        dis "{c O/}"  
 
           
*== 具有可链接目录的帮助文件 ==

  help ranktest
  viewsource ranktest.hlp
  
  * 第一步：设定段落标签
  * 第二部：引用此前设定的标签
  
  * {title:Contents}
  * {p 2}{help ranktest##s_description:Description}{p_end}

  * {marker s_description}{title:Description}
  
  help smcl
  viewsource smcl.sthlp


*== 具有可运行实例的帮助文件 ==

  help rbc
  viewsource rbc.hlp
  
  dis "{cmd: sysuse auto,clear}{stata sysuse auto, clear: click here to run}"
  dis "{hi:Example:}{break}{space 10}{cmd:.rbc,v(y c w)}{space 16}{stata rbc,v(y c w):click here to run}"
  
  * 说明：如果实例包含多行命令，则可以先将这些命令封装成一个简单的命令，另存。
  help scatter
  viewsource scatter.sthlp

  * 建议：在完成一个新程序的编写后，要尽快编写帮助文件，并尽可能详细，
  *       否则日后很可能连自己的不知道该程序的具体使用方法了。
  
  
                 *== 成为编程高手的密诀：？？

  
  
  
  
                  *  反复练习，多加总结！
                  
                  * (1) 从最简单的程序开始：素描；
                  * (2) Stata程序是很好的参考资料；
                  * (3) 对于复杂程序，要多花时间进行结构的分析和设计；
        
